home *** CD-ROM | disk | FTP | other *** search
- /*
- File: StandardGetFolder.c
-
- Contains: Routines needed to use CustomGetFile for selecting folders
- Also adds a button to create new folders.
-
- Written by: Andy Bachorski
-
- Copyright: Copyright © 1998-1999 by Apple Computer, Inc., All Rights Reserved.
-
- You may incorporate this Apple sample source code into your program(s) without
- restriction. This Apple sample source code has been provided "AS IS" and the
- responsibility for its operation is yours. You are not permitted to redistribute
- this Apple sample source code as "Apple sample source code" after having made
- changes. If you're going to re-distribute the source, we require that you make
- it clear in the source that the code was descended from Apple sample source
- code, but that you've made changes.
-
- A Few Notes:
- The routines in this file have been tested, though not exhaustively so.
- They should not be used as the basis of the final code you use in your
- application without further testing.
-
- The error reporting is fairly complete, but there could be errors encounter
- which deserve a more descriptive message than is displayed here.
-
- This code assumes the presence of System 7 or later. You should check
- the system version before using these routines in your code.
-
- The MoreFiles package is used for some of the sanity checking in the routines,
- and is not included with this project. You will need to obtain the
- MoreFiles package (from the developer ftp site) to build an use this project.
-
- The New Folder and 'the name is already taken' error dialogs are from the
- Standard File package and are not included with this project, but rather are
- loaded directly from the system. If this makes you uncomfortable, you can
- always copy those dialog to your application before you ship.
-
- Change History (most recent first):
- 7/1/1999 Karl Groethe Updated for Metrowerks Codewarror Pro 2.1
-
- 2/02/99 Andy Bachorski Updated project to CodeWarrior Pro 2
- Added New Folder button to dialog, along
- with the routinesand changes to other
- routines needed to support the button.
- One routine added is a filter to restrict
- the length of the new folder name and to
- filter out ':' characters, which are
- illegal in folder names.
-
-
-
- */
-
- //****************** Universal Interfaces ****************************
-
- #include <Aliases.h>
- #include <Controls.h>
- #include <Folders.h>
- #include <Scrap.h>
- #include <StandardFile.h>
- #include <TextUtils.h>
- #include <ToolUtils.h>
-
- #include "MoreFilesExtras.h"
-
-
- //****************** Project Interfaces ****************************
-
- #include "StandardGetFolder.h"
-
-
- //****************** Private Definitions ****************************
-
- enum {
- kSFGetFolderDlgID = 250, // ID for the CustomFile dialog resource
- kSelectItem = 10, // Item number for select button in this dialog
- kCreateNewFolder = 12, // Item number for new folder button
-
- // Command-key characters should not be localized, so it is OK to hard-code them
- // directly where they are used.
-
- kSelectButtonKey = 's', // Command key char for the select button
- kNewFolderButton = 'n' // Command key char for the new folder button
- };
-
- enum {
- kSelectButtonStrListID = 250, // String list containing labels for select button
- kSelectStrNum = 1, // 'Select: "^0"'
- kDesktopStrNum = 2, // 'Desktop'
- kSelectNoQuoteStrNum = 3 // 'Select: ^0'
- };
-
- enum {
- kErrorStringListID = 251, // String list containing error messages
- errNameAlreadyTakenStr = 1, // Name already taken message
- errGenericIOErrorStr = 2 // Generic I/O error message, for unexpected errors.
- };
-
- enum {
- kSFGetFolderNameDlgID = -6046, // These two dialogs are from the StandardFile package
- kSFFolderNameTaken = -6044, // and are loaded from the System file. If this makes you nervous
- // you can always put a copy of them in your application.
- kSFFolderNameTextItem = 3, // Item number of name entry edit field in kSFGetFolderNameDlgID dialog
- kSFNameTakenTextItem = 2 // Item number of error string in kSFFolderNameTaken dialog
- };
-
- enum {
- kDontUseQuotes = false, // parameter for SetButtonName
- kUseQuotes = true
- };
-
- enum {
- kMaxStringLength = 31, // Length limit for folder names
- kButtonHiliteDelay = 8 // Ticks to delay while flashing buttons for command-key entries.
- };
-
- enum {
- kFilterAllTypes = -1, // pass all types to filter proc in StandardFild get dialog
- kBootVolumeVRefNum = -1
- };
-
- //****************** Private Types ****************************
-
- // This structure contains the data that StandardGetFolder needs to do its thing.
- // It is passed to the standard file hook routine in the refcon when CustomGetFile is called.
-
- struct UserDataRec {
- DialogPtr dialogPtr; // a pointer to the dialog (for access to items in the dialog).
- StandardFileReply *sfrPtr; // a pointer to the standard file reply record (so the current selection can be inspected).
- FSSpec oldSelectionFSSpec; // a copy of the "previous" file spec from the reply record (to check if the selection has changed).
- long desktopDirID; // the dirID
- short desktopVRefNum; // and vRefNum for the desktop folder of the boot volume (to check if the desktop is selected).
- };
- typedef struct UserDataRec UserDataRec;
- typedef UserDataRec *UserDataRecPtr;
-
-
- //****************** Private Prototypes ****************************
-
- pascal Boolean SFGetFolderModalDialogFilter( DialogPtr dialogPtr, EventRecord *eventRecPtr, short *item, Ptr dataPtr );
- pascal short SFGetFolderDialogHook( short item, DialogPtr dialogPtr, Ptr dataPtr );
-
- void CreateNewFolder( DialogPtr sfDlgPtr, UserDataRecPtr userDataRecPtr, short *dialogItem );
- pascal Boolean DialogFilter (DialogPtr inputDialog, EventRecord * dialogEventPtr, short *dialogItemPtr);
- void ShowCreateFolderError( Point dialogPt, OSErr anErr );
-
- void SetSelectButtonName( DialogPtr dialogPtr, UserDataRecPtr userDataRecPtr );
- void SetButtonName( DialogPtr dialogPtr, short buttonID, StringPtr buttonName, Boolean quoteFlag);
- void GetLabelString( StringPtr theStr, short stringNum );
- void GetErrorString( StringPtr theStr, short stringNum );
- void CopyPStr( StringPtr src, StringPtr dest );
- void FlashButton( DialogPtr dialogPtr, short buttonID );
- void SetButtonEnabled( DialogPtr dialogPtr, short buttonID, Boolean enableButton );
- Boolean SameFSSpec( FSSpecPtr spec1, FSSpecPtr spec2 );
- Boolean FilterOutColons( long message );
- Boolean RestrictTextLength( DialogPtr dialogPtr, EventRecord * theDialogEvent, long maxStringLength, short dialogItem );
- short DialogHasSelectionRange( DialogPeek inputDialog );
- Boolean KeyIsEditKey( char theKey );
-
-
-
- //******************************************************************************
- //
- // This routine assumes the presence of CustomGetFile, which is available in
- // System 7 and later. You should check for the right system version before
- // calling this routine.
- //
- pascal void StandardGetFolder( FileFilterYDUPP fileFilter, StandardFileReply *sfReply )
- {
- Point dialogTopLeft = { -1, -1 }; // Use to center the dialog.
- SFTypeList mySFTypeList = { 0 };
- UserDataRec userData;
- DlgHookYDUPP dialogHookUPP;
- ModalFilterYDUPP modalFilterUPP;
- OSErr anErr;
-
- CopyPStr( "\p ", sfReply->sfFile.name ); // Set initial contents of Select.
-
- userData.sfrPtr = sfReply; // Put point to in the reply record in the UserDataRec for later access.
-
- // Find the desktop folder, save it in the user data struct.
-
- anErr = FindFolder( kBootVolumeVRefNum, kDesktopFolderType, kDontCreateFolder,
- &userData.desktopVRefNum, &userData.desktopDirID );
-
- if ( anErr != noErr ) // Can't find desktop folder, use values that won't match any real vRefNum/dirID.
- {
- userData.desktopVRefNum = 0;
- userData.desktopDirID = 0;
- }
-
- // Display the custom choose folder dialog.
-
- dialogHookUPP = NewDlgHookYDProc( SFGetFolderDialogHook ); // These should probably global and initialized at app
- modalFilterUPP = NewModalFilterYDProc( SFGetFolderModalDialogFilter ); // startup time if this routine is call more than once.
-
- CustomGetFile( fileFilter, kFilterAllTypes, mySFTypeList, sfReply, kSFGetFolderDlgID,
- dialogTopLeft, dialogHookUPP, modalFilterUPP, nil, nil, &userData );
-
- DisposeRoutineDescriptor( dialogHookUPP );
- DisposeRoutineDescriptor( modalFilterUPP );
-
- // The user has pressed the select button and no error was returned.
- // Do a bit of post processing to ensure a useable result is returned.
-
- if ( sfReply->sfGood )
- {
- // If the selection's name is null, nothing is selected in the
- // dialog list, so return the parent folder as the result.
-
- if ( sfReply->sfFile.name[0] == '\0' )
- {
- FSSpec parentDirFSS;
-
- anErr = FSMakeFSSpec( sfReply->sfFile.vRefNum, sfReply->sfFile.parID, "\p", &parentDirFSS );
- if (anErr == noErr)
- {
- sfReply->sfFile = parentDirFSS;
- }
- else
- {
- sfReply->sfGood = false; // Can't get a valid FSSpec to return, so set the appropriate condition.
- }
- }
-
- // Everything still looks good, check what type of result is really being returned.
-
- if ( sfReply->sfGood )
- {
- Boolean folderFlag;
- Boolean wasAliasedFlag;
-
- // First, resolve alias files so the FSSpec points to the target of the alias.
-
- anErr = ResolveAliasFile( &sfReply->sfFile, true, &folderFlag, &wasAliasedFlag );
- if ( anErr != noErr ) // Alias couldn't be resolved, set the failure condition.
- {
- sfReply->sfGood = false;
- }
- else if ( folderFlag ) // Got a valid FSSpec, make sure the alias resolved to a folder.
- {
- // Lastly, is the selection a volume?
-
- if ( sfReply->sfFile.parID == fsRtParID ) // The parID of the root of a disk is always fsRtParID == 1
- {
- sfReply->sfIsVolume = true;
- sfReply->sfIsFolder = false;
- }
- else
- {
- sfReply->sfIsVolume = false;
- sfReply->sfIsFolder = true;
- }
- }
- }
- }
- }//end StandardGetFolder
-
- //******************************************************************************
- //
- // Return true (to filter the item) if this item is invisible or a file.
- //
- pascal Boolean OnlyVisibleFoldersCustomFileFilter( CInfoPBPtr myCInfoPBPtr, Ptr dataPtr )
- {
- #pragma unused (dataPtr)
-
- Boolean visibleFlag;
- Boolean folderFlag;
-
- visibleFlag = ! ( myCInfoPBPtr->hFileInfo.ioFlFndrInfo.fdFlags & kIsInvisible );
- folderFlag = ( myCInfoPBPtr->hFileInfo.ioFlAttrib & ioDirMask );
-
- // Because the semantics of the filter proc are "true means don't show
- // it" we need to invert the result that we return
-
- return !( visibleFlag && folderFlag );
- }//end OnlyVisibleFoldersCustomFileFilter
-
- //******************************************************************************
- //
- // SFGetFolderModalDialogFilter maps a key to the Select button, and handles
- // flashing of the button when the key is hit
- //
- pascal Boolean SFGetFolderModalDialogFilter( DialogPtr dialogPtr, EventRecord *eventRecPtr, short *item, Ptr dataPtr )
- {
- #pragma unused ( dataPtr )
-
- Boolean eventHandled = false;
-
- // It is always a good idea to make certain the proper dialog is showing,
- // since there are extension out there that can muck with things, and also
- // because standard file can nest dialogs but calls the same filter for each.
-
- if ( ((WindowPeek)dialogPtr)->refCon == sfMainDialogRefCon )
- {
- // Check if the select button was hit
-
- if ( ( eventRecPtr->what == keyDown ) && ( eventRecPtr->modifiers & cmdKey ) )
- {
- if ( (eventRecPtr->message & charCodeMask) == kSelectButtonKey )
- {
- *item = kSelectItem;
- FlashButton( dialogPtr, kSelectItem );
- eventHandled = true;
- }
- else if ( (eventRecPtr->message & charCodeMask) == kNewFolderButton )
- {
- *item = kCreateNewFolder;
- FlashButton( dialogPtr, kCreateNewFolder );
- eventHandled = true;
- }
- }
- }
-
- return eventHandled;
- }//end SFGetFolderModalDialogFilter
-
- //******************************************************************************
- //
- // The StandardFile hook routine. It ...
- // maps the select button to Open
- // presents the new folder dialog
- // sets the Select button name
- //
- pascal short SFGetFolderDialogHook( short dialogItem, DialogPtr dialogPtr, Ptr dataPtr )
- {
- // Be sure Std File is really showing us the intended dialog, not a nested modal dialog.
-
- if ( ((WindowPeek)dialogPtr)->refCon == sfMainDialogRefCon )
- {
- UserDataRecPtr userDataRecPtr = (UserDataRecPtr)dataPtr;
-
- // Map the Select button to Open
-
- switch ( dialogItem )
- {
- case kSelectItem :
- {
- dialogItem = sfItemOpenButton;
- }
- break;
-
- case sfHookNullEvent :
- if ( SameFSSpec( &userDataRecPtr->sfrPtr->sfFile, &userDataRecPtr->oldSelectionFSSpec ) )
- {
- break;
- }
-
- case sfItemFileListUser :
- {
- OSErr anErr;
- Boolean enableNewFolderButton = true;
- SInt8 ioACUser;
-
- // Is the volume locked?
- anErr = CheckVolLock( nil, userDataRecPtr->sfrPtr->sfFile.vRefNum );
- if ( anErr != noErr )
- {
- enableNewFolderButton = false; // It's locked, no need to go further.
- }
- else // Volume not locked, does it have access restrictions?
- {
- anErr = GetIOACUser( userDataRecPtr->sfrPtr->sfFile.vRefNum, userDataRecPtr->sfrPtr->sfFile.parID,
- userDataRecPtr->sfrPtr->sfFile.name, &ioACUser );
- if ( anErr == noErr )
- {
- if ( ! userHasFullAccess( ioACUser ) )
- {
- enableNewFolderButton = false;
- }
- }
- }
- SetButtonEnabled( dialogPtr, kCreateNewFolder, enableNewFolderButton );
- }
-
- case sfHookFirstCall :
- case sfHookChangeSelection :
- case sfHookRebuildList :
- SetSelectButtonName( dialogPtr, userDataRecPtr );
- break;
-
- case kCreateNewFolder : // The New Folder button has been clicked. Do that new folder dialog 'thang.
- CreateNewFolder( dialogPtr, userDataRecPtr, &dialogItem );
- break;
-
- default : // This case is here for debugging purposes only
- // - provides a place to catch unexpected selectors while debugging.
- if ( dialogItem != sfHookNullEvent )
- {
- dialogItem = dialogItem;
- }
- break;
- }
-
- // Save the current selection as the old selection for comparison next time.
- //
- // It's not valid on the first call, though, or if we don't have a
- // name available from standard file.
-
- if ( dialogItem != sfHookFirstCall || userDataRecPtr->sfrPtr->sfFile.name[0] != '\0')
- {
- userDataRecPtr->oldSelectionFSSpec = userDataRecPtr->sfrPtr->sfFile;
- }
- else
- {
- // On first call, empty string won't set the button correctly, so invalidate oldSelection.
-
- userDataRecPtr->oldSelectionFSSpec.vRefNum = 999;
- userDataRecPtr->oldSelectionFSSpec.parID = 0;
- }
- }
-
- return dialogItem;
- }
-
- //******************************************************************************
-
- enum {
- kHorzOffsetFromNewFolderButtonPos = 55,
- kVertOffsetFromNewFolderButtonPos = 35
- };
-
- void CreateNewFolder( DialogPtr sfDlgPtr, UserDataRecPtr userDataRecPtr, short *dialogItem )
- {
- DialogPtr dialogPtr;
- Point itemPoint;
- OSErr anErr;
-
- dialogPtr = GetNewDialog( kSFGetFolderNameDlgID, nil, (WindowPtr)(-1) ); // -1 means put the dialog in front of all other windows.
- if ( dialogPtr != nil )
- {
- Rect itemRect;
- short itemType;
- Handle itemHandle;
-
- ModalFilterUPP modalFilterUPP = NewModalFilterProc( DialogFilter );
- SInt16 hitItem = 0;
-
- // Set up our user items for various things.
- SetDialogDefaultItem( dialogPtr, ok );
- SetDialogCancelItem( dialogPtr, cancel );
- SetDialogTracksCursor( dialogPtr, true );
-
- // Get the position of the New Folder button so the new folder dialog can be
- // centered there, which more or less matches the default system behavior.
- GetDialogItem( sfDlgPtr, kCreateNewFolder, &itemType, &itemHandle, &itemRect );
-
- itemPoint.h = itemRect.left - kHorzOffsetFromNewFolderButtonPos;
- itemPoint.v = itemRect.top - kVertOffsetFromNewFolderButtonPos;
- LocalToGlobal( &itemPoint );
-
- MoveWindow( (WindowPtr)dialogPtr, itemPoint.h, itemPoint.v, true );
-
- // Select the default folder name.
- SelectDialogItemText( dialogPtr, kSFFolderNameTextItem, 0, 32 );
-
- // Let's see the dialog now.
- ShowWindow( (WindowPtr)dialogPtr );
- DrawDialog( dialogPtr );
-
- // Spin in the dialog 'till one of the buttons is pressed.
- do {
- ModalDialog( modalFilterUPP, &hitItem );
- } while ( hitItem != ok && hitItem != cancel );
-
- if ( hitItem == ok )
- {
- Str31 itemStr;
-
- GetDialogItem( dialogPtr, kSFFolderNameTextItem, &itemType, &itemHandle, &itemRect );
- GetDialogItemText( itemHandle, itemStr );
-
- // Make sure the user has entered a name string to work with.
- if ( itemStr[ 0 ] > '\0' )
- {
- long newDirID;
-
- // Check to see if the item selected is on the desktop. If it is, create the new folder on the desktop.
-
- if ( userDataRecPtr->sfrPtr->sfFile.parID == fsRtParID )
- {
- userDataRecPtr->sfrPtr->sfFile.parID = userDataRecPtr->desktopDirID;
- userDataRecPtr->sfrPtr->sfFile.vRefNum = userDataRecPtr->desktopVRefNum;
- }
-
- BlockMoveData( itemStr, userDataRecPtr->sfrPtr->sfFile.name, itemStr[ 0 ] + 1 );
-
- anErr = FSpDirCreate( &userDataRecPtr->sfrPtr->sfFile, smSystemScript, &newDirID );
- userDataRecPtr->sfrPtr->sfFile.parID = newDirID;
- if ( anErr == noErr )
- {
- *dialogItem = sfHookChangeSelection; // Force a list update
- }
- }
- }
- DisposeDialog( dialogPtr );
- InitCursor(); // Init to prevent I-beam from hanging around in some circumstances.
- DisposeRoutineDescriptor( modalFilterUPP );
- }
- if ( anErr != noErr )
- {
- ShowCreateFolderError( itemPoint, anErr );
- }
-
- return;
- }//end CreateNewFolder
-
- //******************************************************************************
-
- pascal Boolean DialogFilter( DialogPtr inputDialog, EventRecord * dialogEventPtr, short *dialogItemPtr )
- {
- Boolean wasAKey;
- Boolean eventWasHandled = false;
-
- // Get some values here that will be used through-out the filter.
-
- wasAKey = ( (dialogEventPtr->what == keyDown) || (dialogEventPtr->what == autoKey) );
-
- //------------------------------------------------------------------------------
- // First some filtering on key events.
- //------------------------------------------------------------------------------
-
- if ( wasAKey )
- {
- eventWasHandled = FilterOutColons( dialogEventPtr->message );
- if ( eventWasHandled == false )
- {
- eventWasHandled = RestrictTextLength( inputDialog, dialogEventPtr, kMaxStringLength, kSFFolderNameTextItem );
- }
- }
-
- // One final thing to do is to call the standard dialog filter.
- // You MUST call the standard filter if you want any of the standard dialog
- // behaviors to happen. The OK button border, cursor tracking, and the rest
- // ONLY happen if you call the standard dialog filter.
- // Also, the standard dialog filter is only called if returnValue is still
- // false by the time we get here.
-
- if ( eventWasHandled == false )
- {
- ModalFilterUPP theModalProc;
- OSErr myErr;
-
- myErr = GetStdFilterProc( &theModalProc );
- if ( myErr == noErr )
- {
- eventWasHandled = CallModalFilterProc( theModalProc, inputDialog, dialogEventPtr, dialogItemPtr );
- }
- }
-
- return eventWasHandled;
- }// end DialogFilter
-
- //******************************************************************************
-
- enum {
- kHorzOffsetFromNewFolderDialogPos = 14,
- kVertOffsetFromNewFolderDialogPos = 6
- };
-
- void ShowCreateFolderError( Point dialogPt, OSErr anErr )
- {
- DialogPtr dialogPtr;
- Str255 errStr;
- short errStrIndex;
-
- if ( anErr == dupFNErr ) // That name is already taken
- {
- errStrIndex = errNameAlreadyTakenStr;
- }
- else // An unexpected error while creating the folder, so give a generic message.
- {
- errStrIndex = errGenericIOErrorStr;
- }
-
- dialogPtr = GetNewDialog( kSFFolderNameTaken, nil, (WindowPtr)(-1) ); // -1 means put the dialog in front of all other windows.
- if ( dialogPtr != nil )
- {
- Rect itemRect;
- short itemType;
- Handle itemHandle;
- SInt16 hitItem = 0;
-
- // Set up our user items for various things.
- SetDialogDefaultItem( dialogPtr, ok );
- SetDialogTracksCursor( dialogPtr, true );
-
- // Position the dialog so it's shown in the same location as the name entry dialog was.
- MoveWindow( (WindowPtr)dialogPtr, dialogPt.h - kHorzOffsetFromNewFolderDialogPos, dialogPt.v - kVertOffsetFromNewFolderDialogPos, true );
-
- // Put our error message in the dialog's text item.
- GetErrorString( errStr, errStrIndex );
- GetDialogItem( dialogPtr, kSFNameTakenTextItem, &itemType, &itemHandle, &itemRect );
- SetDialogItemText( itemHandle, errStr );
-
- // Let's see the dialog now.
- ShowWindow( (WindowPtr)dialogPtr );
- DrawDialog( dialogPtr );
-
- ModalDialog( nil, &hitItem );
-
- DisposeDialog( dialogPtr );
- }
-
- return;
- }//end ShowCreateFolderError
-
- //******************************************************************************
-
- void SetSelectButtonName( DialogPtr dialogPtr, UserDataRecPtr userDataRecPtr )
- {
- // Be sure there is a file name selected.
-
- if ( userDataRecPtr->sfrPtr->sfFile.name[ 0 ] != '\0' )
- {
- SetButtonName( dialogPtr, kSelectItem,
- userDataRecPtr->sfrPtr->sfFile.name, kUseQuotes );
- }
- else // No file name in the reply, so...
- {
- // Is the desktop selected?
-
- if ( userDataRecPtr->sfrPtr->sfFile.vRefNum == userDataRecPtr->desktopVRefNum
- && userDataRecPtr->sfrPtr->sfFile.parID == userDataRecPtr->desktopDirID )
- {
- // Set button to "Select Desktop".
-
- Str63 desktopName;
-
- GetLabelString( desktopName, kDesktopStrNum );
- SetButtonName( dialogPtr, kSelectItem, desktopName, kDontUseQuotes );
- }
- else // Desktop not selected, so there is nothing in the folder selected.
- { // Get parent directory's name for the Select button.
- // Passing an empty name string to FSMakeFSSpec gets the name of the folder specified by the parID parameter
-
- FSSpec parentFSS;
-
-
- (void) FSMakeFSSpec( userDataRecPtr->sfrPtr->sfFile.vRefNum,
- userDataRecPtr->sfrPtr->sfFile.parID, "\p", &parentFSS );
- SetButtonName( dialogPtr, kSelectItem, parentFSS.name, kUseQuotes );
- }
- }
- }//end SetSelectButtonName
-
- //******************************************************************************
- //
- // SetButtonName sets the name of the Select button in the SF dialog.
- //
- // To do this, we need to call the Script Manager to truncate the label in the
- // middle to fit the button and to merge the button name with the word Select
- // (possibly followed by quotes). Using the Script Manager avoids all sorts
- // of problems internationally.
- //
- // buttonName is the name to appear following the word Select
- // quoteFlag should be true if the name is to appear in quotes
- //
- void SetButtonName( DialogPtr dialogPtr, short buttonID, StringPtr buttonName, Boolean quoteFlag)
- {
- short buttonType;
- Handle buttonHandle;
- Rect buttonRect;
- Handle labelHandle;
- Str255 labelStr;
- OSErr anErr;
-
- // Get the details of the button from the dialog
-
- GetDialogItem( dialogPtr, buttonID, &buttonType, &buttonHandle, &buttonRect );
-
- // Get the string for the select button label, "Select ^0" or "Select “^0”"
-
- if ( quoteFlag == kUseQuotes )
- {
- GetLabelString( labelStr, kSelectStrNum );
- }
- else
- {
- GetLabelString( labelStr, kSelectNoQuoteStrNum );
- }
-
- // Make string handles containing the select button label and the
- // file name to be stuffed into the button
-
- anErr = PtrToHand( &labelStr[1], &labelHandle, labelStr[0] );
- if ( anErr == noErr )
- {
- Handle nameHandle = nil;
- short textWidth;
-
- // cut out the middle of the file name to fit the button
- //
- // we'll temporarily use labelStr here to hold the modified button name
- // since we don't own the buttonName string storage space
-
- textWidth = ( buttonRect.right - buttonRect.left ) - StringWidth( labelStr );
-
- CopyPStr( buttonName, labelStr );
- (void) TruncString( textWidth, labelStr, smTruncMiddle );
-
- anErr = PtrToHand( &labelStr[1], &nameHandle, labelStr[0] );
- if ( anErr == noErr )
- {
- Str15 keyStr;
-
- // replace the ^0 in the Select string with the file name
-
- CopyPStr( "\p^0", keyStr );
-
- (void) ReplaceText( labelHandle, nameHandle, keyStr );
-
- labelStr[0] = (unsigned char)GetHandleSize( labelHandle );
- BlockMoveData( *labelHandle, &labelStr[1], labelStr[0] );
-
- // now set the control title, and re-validate the area
- // above the control to avoid a needless redraw
-
- SetControlTitle( (ControlHandle)buttonHandle, labelStr );
-
- ValidRect( &buttonRect );
-
- DisposeHandle( nameHandle );
- }
- DisposeHandle( labelHandle );
- }
-
- return;
- }//end SetButtonName
-
- //******************************************************************************
-
- void GetLabelString( StringPtr theStr, short stringNum )
- {
- GetIndString( theStr, kSelectButtonStrListID, stringNum );
- }
-
- //******************************************************************************
-
- void GetErrorString( StringPtr theStr, short stringNum )
- {
- GetIndString( theStr, kErrorStringListID, stringNum );
- }
-
- //******************************************************************************
-
- void CopyPStr( StringPtr src, StringPtr dest )
- {
- BlockMoveData( src, dest, 1 + src[ 0 ] );
- }
-
- //******************************************************************************
- //
- // FlashButton briefly highlights the dialog button
- // as feedback for key equivalents
- //
- void FlashButton( DialogPtr dialogPtr, short buttonID )
- {
- short buttonType;
- Handle buttonHandle;
- Rect buttonRect;
- UInt32 finalTicks;
-
- GetDialogItem( dialogPtr, buttonID, &buttonType, &buttonHandle, &buttonRect );
-
- HiliteControl((ControlHandle) buttonHandle, kControlButtonPart);
- Delay( kButtonHiliteDelay, &finalTicks );
- HiliteControl((ControlHandle) buttonHandle, 0);
-
- return;
- }//end FlashButton
-
- //******************************************************************************
- //
- void SetButtonEnabled( DialogPtr dialogPtr, short buttonID, Boolean enableButton )
- {
- short buttonType;
- Handle buttonHandle;
- Rect buttonRect;
-
- GetDialogItem( dialogPtr, buttonID, &buttonType, &buttonHandle, &buttonRect );
-
- if ( enableButton )
- {
- HiliteControl( (ControlHandle)buttonHandle, kControlNoPart );
- }
- else
- {
- HiliteControl( (ControlHandle)buttonHandle, kControlInactivePart );
- }
-
- return;
- }//end SetButtonEnabled
-
- //******************************************************************************
-
- Boolean SameFSSpec( FSSpecPtr spec1, FSSpecPtr spec2 )
- {
- return ( spec1->vRefNum == spec2->vRefNum
- && spec1->parID == spec2->parID
- && EqualString( spec1->name, spec2->name, false, false ) );
- }//end SameFSSpec
-
- //******************************************************************************
- //
- // This key filter demonstrates how to disallow certain characters.
- // This particular filter won't allow the entry of numeric characters.
- // Any number entered will be eaten, and a beep generated.
- //
- Boolean FilterOutColons( long message )
- {
- char theKeyPressed = message & charCodeMask;
- Boolean keyWasFiltered = false;
-
- if ( theKeyPressed == ':' )
- {
- // Dang, it's a colon, reject it.
- keyWasFiltered = true;
- }
-
- return keyWasFiltered;
- }// end FilterOutColons
-
- //**************************************************************************************
- //
- // This key filter restricts the length of text in an edit field.
- //
- // The paste command needs to be handled here, since it can cause the text in the edit
- // field to go over the length limit.
- // This filter also ckecks for and passed through keyboard navigation keys in the case where
- // the edit field is at its maximum length.
- //
- Boolean RestrictTextLength( DialogPtr dialogPtr, EventRecord * theDialogEvent, long maxStringLength, short dialogItem )
- {
- Rect itemRect;
- Handle itemHandle;
- Str255 itemStr;
-
- short itemType;
- short selectionLength;
- char theKeyPressed;
-
- Boolean keyWasFiltered = false;
-
- selectionLength = DialogHasSelectionRange( (DialogPeek)dialogPtr );
-
- // Get the text from the edit field.
- GetDialogItem( dialogPtr, dialogItem, &itemType, &itemHandle, &itemRect );
- GetDialogItemText( itemHandle, itemStr );
-
- theKeyPressed = theDialogEvent->message & charCodeMask;
-
- if (theDialogEvent->modifiers & cmdKey) // Was the key pressed a command?
- {
-
- if ( (theKeyPressed == 'v') || (theKeyPressed == 'V') ) // Was it a paste command? ( ** NOT LOCALIZED ** )
- {
- long offset;
- long scrapLen = GetScrap( nil, 'TEXT', &offset );
- long newStrLen = itemStr[0] + (scrapLen - selectionLength);
-
- if ( newStrLen > maxStringLength ) // Yes, will the paste exceed the max length?
- {
- keyWasFiltered = true;
- }
- }
- }
- else if ( ! KeyIsEditKey( theKeyPressed ) && (itemStr[0] - selectionLength + 1) > maxStringLength )
- {
- keyWasFiltered = true;
- }
-
- return keyWasFiltered;
- }// end RestrictTextLength
-
- //******************************************************************************
-
- short DialogHasSelectionRange( DialogPeek inputDialog )
- {
- TEHandle theTERecord;
- short selectionRange;
-
- theTERecord = inputDialog->textH;
- selectionRange = (*theTERecord)->selEnd - (*theTERecord)->selStart;
-
- return ( selectionRange );
- }// end DialogHasSelectionRange
-
- //******************************************************************************
- //
- // A little utility to see if the current key is an edit-type key
- //
- // key equates
- enum
- {
- kEnterKey = 0x03,
- kTabKey = 0x09,
- kReturnKey = 0x0D,
- kBackSpace = 0x08,
- kEscKey = 0x1B,
- kLeftArrow = 0x1C,
- kRightArrow = 0x1D,
- kUpArrow = 0x1E,
- kDownArrow = 0x1F,
- kDeleteKey = 0x7F
- };
-
- Boolean KeyIsEditKey( char theKey )
- {
- long index;
- Boolean isEditKey = false;
-
- char editChars[] = { kLeftArrow, kUpArrow, kRightArrow, kDownArrow, kBackSpace, kEscKey, kTabKey, kEnterKey };
- long editCharCount = sizeof( editChars ) / sizeof( char );
-
- for ( index = 0; index < editCharCount; index++ )
- {
- if ( theKey == editChars[ index ] )
- {
- isEditKey = true;
- }
- }
-
- return (isEditKey);
- }// end KeyIsEditKey
-
- //******************************************************************************
-